#include "lcd1602.h"
#include "c51.h"
#include "utils.h"

//引脚配置：
#define LCD_RS P2_6
#define LCD_RW P2_5
#define LCD_EN P2_7

#define LCD_DataPort P0

/**
 * @brief  LCD1602写命令
 * @param  Command 要写入的命令
 * @retval 无
 */
void lcd_write_command(u8 command) {
  LCD_RS = 0;
  LCD_RW = 0;
  LCD_DataPort = command;
  LCD_EN = 1;
  delay(1);
  LCD_EN = 0;
  delay(1);
}

/**
 * @brief  LCD1602写数据
 * @param  Data 要写入的数据
 * @retval 无
 */
void lcd_write_data(u8 data) {
  LCD_RS = 1;
  LCD_RW = 0;
  LCD_DataPort = data;
  LCD_EN = 1;
  delay(1);
  LCD_EN = 0;
  delay(1);
}

/**
 * @brief  LCD1602设置光标位置
 * @param  Line 行位置，范围：1~2
 * @param  Column 列位置，范围：1~16
 * @retval 无
 */
void lcd_set_cursor(u8 line, u8 column) {
  if (line == 1) {
    lcd_write_command(0x80 | (column - 1));
  } else if (line == 2) {
    lcd_write_command(0x80 | (column - 1 + 0x40));
  }
}

/**
 * @brief  LCD1602初始化函数
 * @param  无
 * @retval 无
 */
void lcd_init(void) {
  lcd_write_command(0x38); // 8bit 数据接口，两行显示，5*7点阵
  lcd_write_command(0x0c); // 显示开，光标关，闪烁关
  lcd_write_command(0x06); // 数据读写操作后，光标自动加一，画面不动
  lcd_write_command(0x01); // 光标复位，清屏
}

/**
 * @brief  在LCD1602指定位置上显示一个字符
 * @param  Line 行位置，范围：1~2
 * @param  Column 列位置，范围：1~16
 * @param  Char 要显示的字符
 * @retval 无
 */
void lcd_show_char(u8 line, u8 column, i8 c) {
  lcd_set_cursor(line, column);
  lcd_write_data(c);
}

/**
 * @brief  在LCD1602指定位置开始显示所给字符串
 * @param  Line 起始行位置，范围：1~2
 * @param  Column 起始列位置，范围：1~16
 * @param  String 要显示的字符串
 * @retval 无
 */
void lcd_show_string(u8 line, u8 column, i8 *str) {
  u8 i;
  lcd_set_cursor(line, column);
  for (i = 0; str[i] != '\0'; i++) {
    lcd_write_data(str[i]);
  }
}

/**
 * @brief  返回值=X的Y次方
 */
i16 lcd_pow(i16 x, i16 y) {
  u8 i;
  i16 result = 1;
  for (i = 0; i < y; i++) {
    result *= x;
  }
  return result;
}

/**
 * @brief  在LCD1602指定位置开始显示所给数字
 * @param  Line 起始行位置，范围：1~2
 * @param  Column 起始列位置，范围：1~16
 * @param  Number 要显示的数字，范围：0~65535
 * @param  Length 要显示数字的长度，范围：1~5
 * @retval 无
 */
void lcd_show_num(u8 line, u8 column, u16 number, u8 len) {
  u8 i;
  lcd_set_cursor(line, column);
  for (i = len; i > 0; i--) {
    lcd_write_data(number / lcd_pow(10, i - 1) % 10 + '0');
  }
}

/**
 * @brief  在LCD1602指定位置开始以有符号十进制显示所给数字
 * @param  Line 起始行位置，范围：1~2
 * @param  Column 起始列位置，范围：1~16
 * @param  Number 要显示的数字，范围：-32768~32767
 * @param  Length 要显示数字的长度，范围：1~5
 * @retval 无
 */
void lcd_show_signed_num(u8 line, u8 column, i16 number, u8 length) {
  u8 i;
  i16 number1;
  lcd_set_cursor(line, column);
  if (number >= 0) {
    lcd_write_data('+');
    number1 = number;
  } else {
    lcd_write_data('-');
    number1 = -number;
  }
  for (i = length; i > 0; i--) {
    lcd_write_data(number1 / lcd_pow(10, i - 1) % 10 + '0');
  }
}

/**
 * @brief  在LCD1602指定位置开始以十六进制显示所给数字
 * @param  Line 起始行位置，范围：1~2
 * @param  Column 起始列位置，范围：1~16
 * @param  Number 要显示的数字，范围：0~0xFFFF
 * @param  Length 要显示数字的长度，范围：1~4
 * @retval 无
 */
void lcd_show_hex_num(u8 row, u8 column, u16 number, u8 len) {
  u8 i, single_n;
  lcd_set_cursor(row, column);
  for (i = len; i > 0; i--) {
    single_n = number / lcd_pow(16, i - 1) % 16;
    if (single_n < 10) {
      lcd_write_data(single_n + '0');
    } else {
      lcd_write_data(single_n - 10 + 'a');
    }
  }
}

/**
 * @brief  在LCD1602指定位置开始以二进制显示所给数字
 * @param  Line 起始行位置，范围：1~2
 * @param  Column 起始列位置，范围：1~16
 * @param  Number 要显示的数字，范围：0~1111 1111 1111 1111
 * @param  Length 要显示数字的长度，范围：1~16
 * @retval 无
 */
void lcd_show_bin_num(u8 line, u8 column, u16 number, u8 length) {
  u8 i;
  lcd_set_cursor(line, column);
  for (i = length; i > 0; i--) {
    lcd_write_data(number / lcd_pow(2, i - 1) % 2 + '0');
  }
}
